package avicit.bdp.dgs.qa.service.algo.process.data;

import avicit.bdp.common.dto.StorageResourceConf;
import avicit.bdp.common.utils.uploads.FileAdapterUtils;
import avicit.bdp.dgs.qa.dao.QaDataSourceDAO;
import avicit.bdp.dgs.qa.dto.*;
import avicit.bdp.dgs.qa.service.algo.IAlgo;
import avicit.bdp.dgs.qa.utils.constant.QaConstant;
import avicit.platform6.core.exception.BusinessException;
import avicit.platform6.core.rest.msg.ResponseMsg;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.opencsv.CSVParserBuilder;
import com.opencsv.CSVReader;
import com.opencsv.CSVReaderBuilder;
import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jetty.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @金航数码科技有限责任公司
 * @作者：developer
 * @邮箱：developer@avic-digital.com
 * @创建时间： 2023-10-27
 * @类说明：ProcessDataAlgo
 * @修改记录：
 * @注意事项：
 * @主要功能：工程值文件校验算法
 */
@Component("processData")
public class ProcessDataAlgo implements IAlgo {
    private static final Logger logger = LoggerFactory.getLogger(ProcessDataAlgo.class);

    @Autowired
    private QaDataSourceDAO qaDataSourceDAO;

    /**
     * 执行算法
     *
     * @param jobDTO   任务信息
     * @param ruleList 规则信息
     * @return 执行结果
     */
    @Override
    public ResponseMsg<List<FtdpFileDTO>> execute(JobDTO jobDTO, List<RuleDTO> ruleList) {
        List<FtdpFileDTO> fileList = null;
        ResponseMsg<List<FtdpFileDTO>> responseMsg = new ResponseMsg<>();

        try {
            // 合法性检查
            validate(jobDTO, ruleList);

            // 获取待校验文件列表
            fileList = getFileList(jobDTO.getResourceIds());
//            List<FtdpFileDTO> fileList = mockFileList();

            // 获取文件模板列表
            List<FCTemplateDTO> fileTemplateList = getFCTemplateList();
//            List<FCTemplateDTO> fileTemplateList = mockFileTemplate();

            // 遍历文件，一一匹配文件模板
            executeInner(jobDTO, ruleList, fileList, fileTemplateList);
        } catch (BusinessException e) {
            setResponse(responseMsg, HttpStatus.INTERNAL_SERVER_ERROR_500, e.getMessage(), fileList);
            return responseMsg;
        }

        setResponse(responseMsg, HttpStatus.OK_200, null, fileList);

        return responseMsg;
    }

    /**
     * 合法性检查
     *
     * @param jobDTO
     * @param ruleList
     * @return
     */
    private void validate(JobDTO jobDTO, List<RuleDTO> ruleList) {
        if (jobDTO == null) {
            logger.error("作业为null");
            throw new BusinessException("作业为null，文件校验失败");
        }
        if (CollectionUtils.isEmpty(ruleList)) {
            // 未能自动匹配到模板，表明当前文件不是.txt或.csv格式
            logger.error("当前仅支持.txt或.csv格式的文件");
            throw new BusinessException("当前仅支持.txt或.csv格式的文件，校验失败");
        }
    }

    /**
     * 获取待校验文件列表
     *
     * @param resourceIds
     * @return
     */
    private List<FtdpFileDTO> getFileList(String resourceIds) {
        if (StringUtils.isEmpty(resourceIds)) {
            return new ArrayList<>();
        }

        List<FtdpFileDTO> fileList = this.qaDataSourceDAO.getFileList(null, Arrays.asList(resourceIds.trim().split(QaConstant.COMMA)));
        if (CollectionUtils.isEmpty(fileList)) {
            return new ArrayList<>();
        }

        return fileList;
    }

    /**
     * 试飞工程值数据校验，获取文件采集-文件模板
     *
     * @return
     */
    private List<FCTemplateDTO> getFCTemplateList() {
        // 设置文件采集-文件模板信息
        List<FCTemplateDTO> fileTemplateList = this.qaDataSourceDAO.getFileTemplateList();
        if (CollectionUtils.isEmpty(fileTemplateList)) {
            throw new BusinessException("文件采集-文件校验模板列表为空，试飞工程值数据校验失败");
        }

        return fileTemplateList;
    }

    /**
     * 遍历文件列表，一一匹配文件模板
     *
     * @param jobDTO
     * @param ruleList
     * @param fileList
     * @param fileTemplateList
     */
    private void executeInner(JobDTO jobDTO, List<RuleDTO> ruleList, List<FtdpFileDTO> fileList, List<FCTemplateDTO> fileTemplateList) {
        if (CollectionUtils.isEmpty(fileList)) {
            logger.warn(String.format("质量任务[%s]中待校验文件列表为空", jobDTO.getName()));
            return;
        }

        List<String> errMsgList = new ArrayList<>();
        for (FtdpFileDTO file : fileList) {
            String fileName = file.getName();
            if (StringUtils.isEmpty(fileName)) {
                logger.error("未配置文件名称，校验失败");
                errMsgList.add("未配置文件名称，校验失败");
                continue;
            }
            if (!isCsvSuffix(fileName)) {
                logger.error(String.format("质量任务[%s]中，文件[%s]不是csv或txt格式。", jobDTO.getName(), fileName));
                errMsgList.add(String.format("文件[%s]不是csv或txt格式，校验失败", fileName));
                continue;
            }

            boolean isMatch = false;
            for (FCTemplateDTO fileTemplate : fileTemplateList) {
                if (fileTemplate == null) {
                    continue;
                }

                if (matchFileTemplate(file.getFullName(), fileTemplate)) {
                    logger.info(String.format("文件[%s]和模板[%s]对应，校验成功", fileName, fileTemplate.getName()));
                    isMatch = true;
                    break;
                }
            }

            if (!isMatch) {
                errMsgList.add(String.format("匹配模板失败，文件[%s]校验失败", fileName));
            }
        }

        if (CollectionUtils.isNotEmpty(errMsgList)) {
            throw new BusinessException(JSONObject.toJSONString(errMsgList));
        }
    }

    /**
     * 判断文件是否为csv格式，包括.csv、.txt，统一按照csv格式处理
     *
     * @param fileName
     * @return
     */
    private boolean isCsvSuffix(String fileName) {
        int pos = fileName.lastIndexOf(".");
        if (pos == -1) {
            return false;
        }

        String suffix = fileName.substring(pos + 1);
        if (StringUtils.isEmpty(suffix)) {
            return false;
        }

        return suffix.toLowerCase().equals("csv") || suffix.toLowerCase().equals("txt");
    }

    /**
     * 判断文件模板中字段是否全部包含在表头中，如果是返回true，否则返回false
     *
     * @param fileName
     * @param fileTemplate
     * @return
     * @throws IOException
     */
    private boolean matchFileTemplate(String fileName, FCTemplateDTO fileTemplate) {
        // 获取文件分隔符
        String templateName = fileTemplate.getName();
        String fileSeparator = fileTemplate.getFileSeparator();
        if (StringUtils.isEmpty(fileSeparator)) {
            logger.error(String.format("模板[%s]未配置分隔符", templateName));
            return false;
        }

        // 获取文件模板列表
        List<FCTemplateFieldDTO> fieldList = fileTemplate.getFieldList();
        if (CollectionUtils.isEmpty(fieldList)) {
            logger.error(String.format("模板[%s]未配置字段信息", templateName));
            return false;
        }

        // 获取文件流
        InputStream ins = getInputStream(fileName);
        if (ins == null) {
            logger.error(String.format("读取文件[%s]失败", fileName));
            return false;
        }

        // 获取文件模板中字段列表
        Set<String> fieldSet = new HashSet<>();
        for (FCTemplateFieldDTO templateField : fieldList) {
            String field = templateField.getName();
            if (StringUtils.isNotEmpty(field)) {
                fieldSet.add(field);
            }
        }
        logger.info(String.format("模板[%s]中字段信息为%s", templateName, String.join(",", fieldSet)));

        try (CSVReader csvReader = new CSVReaderBuilder(new InputStreamReader(ins, StandardCharsets.UTF_8))
                .withCSVParser(new CSVParserBuilder().withSeparator(fileSeparator.charAt(0)).build())
                .withSkipLines(0)
                .build()) {
            // 读取表头，并判断文件模板中字段信息是否全部包含在表头中
            String[] nextLine = null;
            while ((nextLine = csvReader.readNext()) != null) {
                if (nextLine == null) {
                    continue;
                }

                Set<String> columnSet = new HashSet<>(Arrays.asList(nextLine));
                for (String field : fieldSet) {
                    if (!columnSet.contains(field)) {
                        logger.error(String.format("模板[%s]中字段[%s]未包含在文件[%s]中", templateName, field, fileName));
                        return false;
                    }
                }

                break;
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            close(ins);
        }

        return true;
    }

    /**
     * 获取文件流
     *
     * @return
     */
    private InputStream getInputStream(String fileName) {
        // 获取minio连接信息
        StorageResourceConf conf = FileAdapterUtils.getStorageResourceByProjectId(null);
        String endPoint = conf.getEndPoint();
        String accessKey = conf.getAccessKey();
        String secretKey = conf.getSecretKey();
        String bucket = conf.getStoragePath();
//        String endPoint = "http://10.216.92.71:9000";
//        String accessKey = "minioadmin";
//        String secretKey = "minioadmin";
//        String bucket = "test";

        // 获取文件流
        InputStream ins = null;
        MinioClient minioClient = MinioClient.builder().endpoint(endPoint).credentials(accessKey, secretKey).build();
        try {
            ins = minioClient.getObject(GetObjectArgs.builder().bucket(bucket).object(fileName).build());
        } catch (Exception e) {
            String msg = new StringBuilder()
                    .append("读取文件").append(fileName).append("失败,")
                    .append("endPoint:").append(endPoint)
                    .append(",accessKey:").append(accessKey)
                    .append(",secretKey:").append(secretKey)
                    .append(",bucket:").append(bucket)
                    .append(",fileName:").append(fileName)
                    .toString();
            throw new BusinessException(msg, e);
        }

        return ins;
    }

    /**
     * 关闭文件流
     *
     * @param ins
     */
    private void close(InputStream ins) {
        if (ins != null) {
            try {
                ins.close();
            } catch (IOException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 设置返回消息
     *
     * @param responseMsg
     * @param retCode
     * @param errMsg
     * @param fileList
     */
    private void setResponse(ResponseMsg<List<FtdpFileDTO>> responseMsg, Integer retCode, String errMsg, List<FtdpFileDTO> fileList) {
        if (responseMsg.getResponseBody() == null) {
            responseMsg.setResponseBody(new ArrayList<>());
        }

        if (StringUtils.isNotEmpty(errMsg)) {
            responseMsg.setErrorDesc(errMsg);
        }
        if (CollectionUtils.isNotEmpty(fileList)) {
            responseMsg.setResponseBody(fileList);
        }

        responseMsg.setRetCode(Integer.toString(retCode));
    }

    /************************************* UT单元测试 *************************************/

    /**
     * 模拟文件列表
     *
     * @return
     */
    private List<FtdpFileDTO> mockFileList() {
        String fileJsonString1 = "{\n" +
                "\t\"fullName\": \"test.txt\",\n" +
                "\t\"pid\": \"8a58bcf68b45edaa018b6b2693141df1\",\n" +
                "\t\"type\": 0,\n" +
                "\t\"directory\": 0,\n" +
                "\t\"size\": 36,\n" +
                "\t\"name\": \"test.txt\",\n" +
                "\t\"fileId\": \"8a58bcf68b45edaa018b6b2694591df3\"\n" +
                "}";
        String fileJsonString2 = "{\n" +
                "\t\"fullName\": \"train1.csv\",\n" +
                "\t\"pid\": \"8a58bcf68b45edaa018b6b2693141df1\",\n" +
                "\t\"type\": 0,\n" +
                "\t\"directory\": 0,\n" +
                "\t\"size\": 1462,\n" +
                "\t\"name\": \"train1.csv\",\n" +
                "\t\"fileId\": \"8a58bcf68b45edaa018b6b2694991df4\"\n" +
                "}";
        String fileJsonString3 = "{\n" +
                "\t\"fullName\": \"train1.pcm\",\n" +
                "\t\"pid\": \"8a58bcf68b45edaa018b6b2693141df1\",\n" +
                "\t\"type\": 0,\n" +
                "\t\"directory\": 0,\n" +
                "\t\"size\": 1462,\n" +
                "\t\"name\": \"train1.pcm\",\n" +
                "\t\"fileId\": \"8a58bcf68b45edaa018b6b2694991df6\"\n" +
                "}";
        List<FtdpFileDTO> fileList = new ArrayList<>();

        fileList.add(JSONObject.parseObject(fileJsonString1, FtdpFileDTO.class));
        fileList.add(JSONObject.parseObject(fileJsonString2, FtdpFileDTO.class));
        fileList.add(JSONObject.parseObject(fileJsonString3, FtdpFileDTO.class));

        return fileList;
    }

    /**
     * 模拟文件模板
     *
     * @return
     */
    private List<FCTemplateDTO> mockFileTemplate() {
        String templateJsonString = "[{\n" +
                "\t\"code\": \"2\",\n" +
                "\t\"fieldList\": [{\n" +
                "\t\t\"id\": \"8a58bcf68b5b6183018b6b50d1e300a1\",\n" +
                "\t\t\"name\": \"abc\",\n" +
                "\t\t\"templateId\": \"8a58a6c787c1e8e30187e4b3edd404ca\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}, {\n" +
                "\t\t\"id\": \"8a58bcf68b5b6183018b6b50d1e300a2\",\n" +
                "\t\t\"name\": \"def\",\n" +
                "\t\t\"templateId\": \"8a58a6c787c1e8e30187e4b3edd404ca\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}, {\n" +
                "\t\t\"id\": \"8a58bcf68b5b6183018b6b50d1e300a3\",\n" +
                "\t\t\"name\": \"ghi\",\n" +
                "\t\t\"templateId\": \"8a58a6c787c1e8e30187e4b3edd404ca\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}],\n" +
                "\t\"fileSeparator\": \"|\",\n" +
                "\t\"id\": \"8a58a6c787c1e8e30187e4b3edd404ca\",\n" +
                "\t\"name\": \"csv文件校验模板\",\n" +
                "\t\"startLine\": \"2\",\n" +
                "\t\"status\": 1\n" +
                "}, {\n" +
                "\t\"code\": \"2\",\n" +
                "\t\"fieldList\": [{\n" +
                "\t\t\"id\": \"8a58a6c78855dac90188571b351115d5\",\n" +
                "\t\t\"name\": \"name\",\n" +
                "\t\t\"templateId\": \"8a58a6c78855dac9018857145e8d15ca\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}, {\n" +
                "\t\t\"id\": \"8a58a6c78855dac90188571b351115d6\",\n" +
                "\t\t\"name\": \"age\",\n" +
                "\t\t\"templateId\": \"8a58a6c78855dac9018857145e8d15ca\",\n" +
                "\t\t\"type\": \"INTEGER\"\n" +
                "\t}, {\n" +
                "\t\t\"id\": \"8a58a6c78855dac90188571b351115d7\",\n" +
                "\t\t\"name\": \"work\",\n" +
                "\t\t\"templateId\": \"8a58a6c78855dac9018857145e8d15ca\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}, {\n" +
                "\t\t\"id\": \"8a58a6c78855dac90188571b351215d8\",\n" +
                "\t\t\"name\": \"message\",\n" +
                "\t\t\"templateId\": \"8a58a6c78855dac9018857145e8d15ca\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}],\n" +
                "\t\"fileSeparator\": \",\",\n" +
                "\t\"id\": \"8a58a6c78855dac9018857145e8d15ca\",\n" +
                "\t\"name\": \"lyb_csv模板\",\n" +
                "\t\"startLine\": \"2\",\n" +
                "\t\"status\": 1\n" +
                "}, {\n" +
                "\t\"code\": \"2\",\n" +
                "\t\"fieldList\": [{\n" +
                "\t\t\"id\": \"8a58a6c78855dac90188655fd7082299\",\n" +
                "\t\t\"name\": \"C1\",\n" +
                "\t\t\"templateId\": \"8a58a6c78855dac90188655fd7072298\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}, {\n" +
                "\t\t\"id\": \"8a58a6c78855dac90188655fd708229a\",\n" +
                "\t\t\"name\": \"C2\",\n" +
                "\t\t\"templateId\": \"8a58a6c78855dac90188655fd7072298\",\n" +
                "\t\t\"type\": \"INTEGER\"\n" +
                "\t}, {\n" +
                "\t\t\"id\": \"8a58a6c78855dac90188655fd708229b\",\n" +
                "\t\t\"name\": \"C3\",\n" +
                "\t\t\"templateId\": \"8a58a6c78855dac90188655fd7072298\",\n" +
                "\t\t\"type\": \"DOUBLE\"\n" +
                "\t}],\n" +
                "\t\"fileSeparator\": \",\",\n" +
                "\t\"id\": \"8a58a6c78855dac90188655fd7072298\",\n" +
                "\t\"name\": \"1111\",\n" +
                "\t\"startLine\": \"2\",\n" +
                "\t\"status\": 1\n" +
                "}, {\n" +
                "\t\"code\": \"2\",\n" +
                "\t\"fieldList\": [{\n" +
                "\t\t\"id\": \"8a58a6c7889906bf0188990a4e8b0020\",\n" +
                "\t\t\"name\": \"id\",\n" +
                "\t\t\"templateId\": \"8a58a6c7889906bf01889908fbd40016\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}, {\n" +
                "\t\t\"id\": \"8a58a6c7889906bf0188990a4e8b0021\",\n" +
                "\t\t\"name\": \"type_code\",\n" +
                "\t\t\"templateId\": \"8a58a6c7889906bf01889908fbd40016\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}, {\n" +
                "\t\t\"id\": \"8a58a6c7889906bf0188990a4e8b0022\",\n" +
                "\t\t\"name\": \"type_name_first\",\n" +
                "\t\t\"templateId\": \"8a58a6c7889906bf01889908fbd40016\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}, {\n" +
                "\t\t\"id\": \"8a58a6c7889906bf0188990a4e8d0023\",\n" +
                "\t\t\"name\": \"type_name_second\",\n" +
                "\t\t\"templateId\": \"8a58a6c7889906bf01889908fbd40016\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}, {\n" +
                "\t\t\"id\": \"8a58a6c7889906bf0188990a4e8d0024\",\n" +
                "\t\t\"name\": \"type_name_third\",\n" +
                "\t\t\"templateId\": \"8a58a6c7889906bf01889908fbd40016\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}, {\n" +
                "\t\t\"id\": \"8a58a6c7889906bf0188990a4e8f0025\",\n" +
                "\t\t\"name\": \"order_num\",\n" +
                "\t\t\"templateId\": \"8a58a6c7889906bf01889908fbd40016\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}, {\n" +
                "\t\t\"id\": \"8a58a6c7889906bf0188990a4e8f0026\",\n" +
                "\t\t\"name\": \"status\",\n" +
                "\t\t\"templateId\": \"8a58a6c7889906bf01889908fbd40016\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}],\n" +
                "\t\"id\": \"8a58a6c7889906bf01889908fbd40016\",\n" +
                "\t\"name\": \"dddd\",\n" +
                "\t\"status\": 1\n" +
                "}, {\n" +
                "\t\"code\": \"2\",\n" +
                "\t\"fieldList\": [{\n" +
                "\t\t\"id\": \"8a58bcf68b4aed2a018b4b9af28305b9\",\n" +
                "\t\t\"name\": \"name\",\n" +
                "\t\t\"templateId\": \"8a58a6c7889906bf01889933061c0279\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}, {\n" +
                "\t\t\"id\": \"8a58bcf68b4aed2a018b4b9af28305ba\",\n" +
                "\t\t\"name\": \"age\",\n" +
                "\t\t\"templateId\": \"8a58a6c7889906bf01889933061c0279\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}, {\n" +
                "\t\t\"id\": \"8a58bcf68b4aed2a018b4b9af28305bb\",\n" +
                "\t\t\"name\": \"work\",\n" +
                "\t\t\"templateId\": \"8a58a6c7889906bf01889933061c0279\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}, {\n" +
                "\t\t\"id\": \"8a58bcf68b4aed2a018b4b9af28305bc\",\n" +
                "\t\t\"name\": \"message\",\n" +
                "\t\t\"templateId\": \"8a58a6c7889906bf01889933061c0279\",\n" +
                "\t\t\"type\": \"STRING\"\n" +
                "\t}],\n" +
                "\t\"fileSeparator\": \",\",\n" +
                "\t\"id\": \"8a58a6c7889906bf01889933061c0279\",\n" +
                "\t\"name\": \"csv模板\",\n" +
                "\t\"startLine\": \"2\",\n" +
                "\t\"status\": 1\n" +
                "}]";
        List<FCTemplateDTO> fileTemplateList = JSON.parseArray(templateJsonString, FCTemplateDTO.class);

        return fileTemplateList;
    }
}
